#!/usr/bin/env python
# -*- mode: python; coding: utf-8; -*-
##--------------------------------------------------------------------------##
##
## Copyright (C) 1998-2003 Markus Oberhumer <markus@oberhumer.com>
## Copyright (C) 2003 Mt. Hood Playing Card Co.
## Copyright (C) 2005-2010 Skomoroh <skomoroh@users.sourceforge.net>
## Copyright (C) 2010-2013 Jeremy Austin-Bardo <tjaustinbardo@gmail.com>
##
## This program is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program.  If not, see <http://www.gnu.org/licenses/>.
##
##--------------------------------------------------------------------------##
"""
CapsStack
  Implement MutableMapping for stack capability with access via keys or
  attributes with methods for basic capability checks.
"""
__all__ = ['CapsStack']

#import logging
from collections import MutableMapping

from ..packlib.info import NO_WRAP, ANY, MAX


class CapsStack(MutableMapping):
    """
    Implement MutableMapping for stack capability with access via keys or
    attributes with methods for basic capability checks.
    """
    __slots__ = ['suit', 'color', 'rank', 'force', 'base_suit', 'base_color',
                 'base_rank', 'base_force', 'dir', 'mod', 'max_move',
                 'min_move', 'max_accept', 'min_accept', 'min_cards',
                 'max_cards']

#    def __new__(self, **kwargs):
#        resource_ = super(CapsStack, self).__new__(self, **kwargs)
#        return resource_

    def __init__(self, suit=ANY, color=ANY, rank=ANY, force=ANY,
                 base_suit=ANY, base_color=ANY, base_rank=ANY, base_force=ANY,
                 dir=0, mod=NO_WRAP, max_move=0, max_accept=0, max_cards=MAX,
                 min_move=1, min_accept=1, min_cards=0):
        """
        __init__([*capabilty* {suit:int=ANY, color:int=ANY, rank:int=ANY,
          force=ANY, base_suit:int=ANY, base_color:int=ANY, base_rank:int=ANY,
          base_force=ANY, dir:int=0, mod:int=NO_WRAP, max_move:int=0,
          max_accept:int=0, max_cards:int=MAX, min_move:int=1,
          min_accept:int=1, min_cards:int=0}]) -> None

        :keyword integer suit: Any card requires this suit.
        :keyword integer color: Any card requires this color.
        :keyword integer rank: Any card requires this rank.
        :keyword integer force: Any card requires this force.
        :keyword integer base_suit: Base card requires this suit.
        :keyword integer base_color: Base card requires this color.
        :keyword integer base_rank: Base card requires this rank.
        :keyword integer base_force: Base card requires this force.
        :keyword integer dir: Value build next rank. (rank + value)
        :keyword integer mod: Value wrap ranks back. (rank % value)
        :keyword integer max_move: Move at most cards at a time.
        :keyword integer max_accept: Accept at most cards at a time.
        :keyword integer max_cards: Total cards not exceed.
        :keyword integer min_move: Move at least cards at a time.
        :keyword integer min_accept: Accept at least cards at a time.
        :keyword integer min_cards: Total cards least requires.
        """
        values = (suit, color, rank, force, base_suit, base_color, base_rank,
                  base_force, dir, mod, max_move, min_move, max_accept,
                  min_accept, min_cards, max_cards, )
        for key, value in zip(self.__slots__, values):
            self.__setValue(key, value)

    def __setValue(self, key, value):
        """
        Check before setting value is an integer and between ANY and MAX

        :param string key: capability attribute name.
        :param integer value: capability attribute value.
        """
        assert type(value) is int and ANY <= value <= MAX
        self.__dict__[key] = value

    def __len__(self):
        """
        :return: number of capabilities.
        :rtype: integer
        """
        return len(self.__slots__)

    def __iter__(self):
        """
        :return: iteration of capabilities.
        :rtype: iterator
        """
        return (key for key in self.__slots__)

    def __repr__(self):
        """
        :return: name / value pairings of capabilities.
        :rtype: string
        """
        return '%s (%r)' % (self.__class__.__name__, self.__dict__)

    def __getitem__(self, key):
        """
        :param string key: capability attribute name.
        :return: capability attribute value.
        :rtype: integer
        """
        return self.__dict__.get(key)

    def __setitem__(self, key, value):
        """
        Calls __setvalue() before setting value.

        :param string key: capability attribute name.
        :param integer value: capability attribute value.
        """
        self.__setValue(key, value)

    def __delitem__(self, key):
        """
        Ignore ability unset capabilities.

        :param string key: capability attribute name.
        """
        pass

    def builds(self, pile, from_pile, cards):
        """
        builds(pile:obj, from_pile:obj, cards:seq) -> bool

        Compare our capabilities with face up cards from_stack.

        - from_pile is not pile and not pile.cardsBlock().
        - cards between min and max accepted.
        - cards being added to pile.cards keeps pile less than max_cards.
        - cards face up with correct suit, color, force, and rank.
        - pile top card is face up when has cards.
        - cards base has correct suit, color, force, and rank when no cards.

        :param object pile: recieving pile
        :param object from_pile: sending pile
        :param object cards:
        :return: acceptable?
        :rtype: bool
        """
        if from_pile is pile or pile.cardsBlock():
            return False
        l = len(cards)
        if l < self['min_accept'] or l > self['max_accept']:
            return False
        l += len(pile.cards)
        if l > self['max_cards']:
            return False
        for c in cards:
            if not c.face_up:
                return False
            for cast in ('suit', 'color', 'rank', 'force',):
                if self[cast] >= 0 and c[cast] != self[cast]:
                    return False
#            if self['color'] >= 0 and c.color != self['color']:
#                return False
#            if self['rank'] >= 0 and c.rank != self['rank']:
#                return False
#            if self['force'] >= 0 and c.force != self['force']:
#                return False
        if pile.cards:
            return pile.TOPCARD.face_up
        else:
            c = cards[0]
            for cast in ('suit', 'color', 'rank', 'force',):
                base = 'base_{}'.format(cast)
                if self[base] >= 0 and c[cast] != self[base]:
                    return False

#            if self['base_suit'] >= 0 and c.suit != self['base_suit']:
#                return False
#            if self['base_color'] >= 0 and c.color != self['base_color']:
#                return False
#            if self['base_rank'] >= 0 and c.rank != self['base_rank']:
#                return False
#            if self['base_force'] >= 0 and c.force != self['base_force']:
#                return False
            return True

    def plays(self, pile, cards):
        """
        plays(pile:obj, cards:seq) -> bool

        Compare our capabilities with our face up cards.

        - not pile.cardsBlock().
        - cards between min and max move.
        - cards being moved leaves pile.cards with more than min_cards.
        - cards face up.

        :param object pile: sending pile
        :param object cards:
        :return: acceptable?
        :rtype: bool
        """
        if pile.cardsBlock():
            return False
        l = len(cards)
        if l < self['min_move'] or l > self['max_move']:
            return False
        if len(pile.cards) - l < self['min_cards']:
            return False
        return all([c.face_up for c in cards]) if cards else False